【新サービス】改竄不能な台帳データベースAmazon QLDBが利用可能になりました
大栗です。
re:Invent 2018で発表されていた台帳データベースのAmazon Quantum Ledger Database(QLDB)が利用可能となったのでご紹介します。
- Now Available – Amazon Quantum Ledger Database (QLDB)
- Announcing General Availability of Amazon Quantum Ledger Database (QLDB)
Amazon Quantum Ledger Database
Amazon Quantum Ledger Database(QLDB)はデータの更新/削除の完全で不変な変更を保持するデータベースです。PartiQLによるSQLクエリをサポートしており、履歴が正当であることを検証できます。銀行などの金融機関やeコマース、輸送物流、人事給与、政府系アプリケーションなどデータの整合性と履歴を維持する必要があるユースケースに適合します。
Hyperledger Fabric、Ethereumなどのブロックチェーンフレームワークにより自前に台帳データベースを運用することも可能ですが、開発や運用が必要となります。Amazon QLDBではマネージドに変更履歴を維持する台帳データベースを簡単に利用できます。
詳しくは、以下のエントリを御覧ください。
[新サービス] フルマネージドの台帳データベースである、Amazon Quantum Ledger Database (QLDB) が発表されました! #reinvent
またSQL互換のPartiQLについてはこちらのエントリを御覧ください。
AWSのデータストアやフォーマットに依存しない問い合わせが可能に。オープンソースとして公開されたSQL互換クエリ言語「PartiQL」を触ってみた
対象リージョン
現在利用可能なリージョンは以下となります。東京が入っていますね。
- 米国東部 (バージニア北部)
- 米国東部 (オハイオ)
- 米国西部 (オレゴン)
- アジアパシフィック (東京)
- EU (アイルランド)
料金
東京リージョンの料金は以下となります。
内容 | 料金 |
---|---|
書き込み I/O | 0.799 USD/100 万リクエスト |
読み込み I/O | 0.155 USD/100 万リクエスト |
ジャーナルストレージ料金 | 月額料金 0.034 USD/GB |
インデックス化ストレージ料金 | 月額料金 0.285 USD/GB |
やってみる
東京リージョンを前提として以下の操作を実施します。
台帳の作成
QLDBのコンソールを開き台帳の作成
をクリックします。
台帳名に一意な名称を入力します。ここではBlogLedger
とします。そして台帳の作成
をクリックします。
しばらくすると台帳が作成されます。作成時刻はUTCで表示されるようです。
サンプルデータの作成
次にサンプルデータを作成します。ここではドキュメントのチュートリアルにある内容を使用します。これは車両登録のデータです。
まずクエリエディタ
を開き、作成した台帳を選択します。
以下の4つのテーブルを作成します。
- VehicleRegistration
- Vehicle
- Person
- DriversLicense
以下のように1テーブルずつ作成して下さい。複文はサポートされていないようです。
CREATE TABLE VehicleRegistration
CREATE TABLE Vehicle
CREATE TABLE Person
CREATE TABLE DriversLicense
実行すると以下のようにtableid
がされます。
次にインデックスを作成します。
CREATE INDEX ON VehicleRegistration (VIN)
CREATE INDEX ON VehicleRegistration (LicensePlateNumber)
CREATE INDEX ON Vehicle (VIN)
CREATE INDEX ON Vehicle (VIN)
CREATE INDEX ON DriversLicense (LicenseNumber)
CREATE INDEX ON DriversLicense (PersonId)
以下のようにインデックスが6個できました。
次にテーブルへデータをロードします。
INSERT INTO Person << { 'FirstName' : 'Raul', 'LastName' : 'Lewis', 'DOB' : `1963-08-19T`, 'GovId' : 'LEWISR261LL', 'GovIdType' : 'Driver License', 'Address' : '1719 University Street, Seattle, WA, 98109' }, { 'FirstName' : 'Brent', 'LastName' : 'Logan', 'DOB' : `1967-07-03T`, 'GovId' : 'LOGANB486CG', 'GovIdType' : 'Driver License', 'Address' : '43 Stockert Hollow Road, Everett, WA, 98203' }, { 'FirstName' : 'Alexis', 'LastName' : 'Pena', 'DOB' : `1974-02-10T`, 'GovId' : '744 849 301', 'GovIdType' : 'SSN', 'Address' : '4058 Melrose Street, Spokane Valley, WA, 99206' }, { 'FirstName' : 'Melvin', 'LastName' : 'Parker', 'DOB' : `1976-05-22T`, 'GovId' : 'P626-168-229-765', 'GovIdType' : 'Passport', 'Address' : '4362 Ryder Avenue, Seattle, WA, 98101' }, { 'FirstName' : 'Salvatore', 'LastName' : 'Spencer', 'DOB' : `1997-11-15T`, 'GovId' : 'S152-780-97-415-0', 'GovIdType' : 'Passport', 'Address' : '4450 Honeysuckle Lane, Seattle, WA, 98101' } >>
INSERT INTO DriversLicense << { 'LicenseNumber' : 'LEWISR261LL', 'LicenseType' : 'Learner', 'ValidFromDate' : `2016-12-20T`, 'ValidToDate' : `2020-11-15T`, 'PersonId' : '' }, { 'LicenseNumber' : 'LOGANB486CG', 'LicenseType' : 'Probationary', 'ValidFromDate' : `2016-04-06T`, 'ValidToDate' : `2020-11-15T`, 'PersonId' : '' }, { 'LicenseNumber' : '744 849 301', 'LicenseType' : 'Full', 'ValidFromDate' : `2017-12-06T`, 'ValidToDate' : `2022-10-15T`, 'PersonId' : '' }, { 'LicenseNumber' : 'P626-168-229-765', 'LicenseType' : 'Learner', 'ValidFromDate' : `2017-08-16T`, 'ValidToDate' : `2021-11-15T`, 'PersonId' : '' }, { 'LicenseNumber' : 'S152-780-97-415-0', 'LicenseType' : 'Probationary', 'ValidFromDate' : `2015-08-15T`, 'ValidToDate' : `2021-08-21T`, 'PersonId' : '' } >>
INSERT INTO VehicleRegistration << { 'VIN' : '1N4AL11D75C109151', 'LicensePlateNumber' : 'LEWISR261LL', 'State' : 'WA', 'City' : 'Seattle', 'PendingPenaltyTicketAmount' : 90.25, 'ValidFromDate' : `2017-08-21T`, 'ValidToDate' : `2020-05-11T`, 'Owners' : { 'PrimaryOwner' : { 'PersonId': '' }, 'SecondaryOwners' : [] } }, { 'VIN' : 'KM8SRDHF6EU074761', 'LicensePlateNumber' : 'CA762X', 'State' : 'WA', 'City' : 'Kent', 'PendingPenaltyTicketAmount' : 130.75, 'ValidFromDate' : `2017-09-14T`, 'ValidToDate' : `2020-06-25T`, 'Owners' : { 'PrimaryOwner' : { 'PersonId': '' }, 'SecondaryOwners' : [] } }, { 'VIN' : '3HGGK5G53FM761765', 'LicensePlateNumber' : 'CD820Z', 'State' : 'WA', 'City' : 'Everett', 'PendingPenaltyTicketAmount' : 442.30, 'ValidFromDate' : `2011-03-17T`, 'ValidToDate' : `2021-03-24T`, 'Owners' : { 'PrimaryOwner' : { 'PersonId': '' }, 'SecondaryOwners' : [] } }, { 'VIN' : '1HVBBAANXWH544237', 'LicensePlateNumber' : 'LS477D', 'State' : 'WA', 'City' : 'Tacoma', 'PendingPenaltyTicketAmount' : 42.20, 'ValidFromDate' : `2011-10-26T`, 'ValidToDate' : `2023-09-25T`, 'Owners' : { 'PrimaryOwner' : { 'PersonId': '' }, 'SecondaryOwners' : [] } }, { 'VIN' : '1C4RJFAG0FC625797', 'LicensePlateNumber' : 'TH393F', 'State' : 'WA', 'City' : 'Olympia', 'PendingPenaltyTicketAmount' : 30.45, 'ValidFromDate' : `2013-09-02T`, 'ValidToDate' : `2024-03-19T`, 'Owners' : { 'PrimaryOwner' : { 'PersonId': '' }, 'SecondaryOwners' : [] } } >>
INSERT INTO Vehicle << { 'VIN' : '1N4AL11D75C109151', 'Type' : 'Sedan', 'Year' : 2011, 'Make' : 'Audi', 'Model' : 'A5', 'Color' : 'Silver' }, { 'VIN' : 'KM8SRDHF6EU074761', 'Type' : 'Sedan', 'Year' : 2015, 'Make' : 'Tesla', 'Model' : 'Model S', 'Color' : 'Blue' }, { 'VIN' : '3HGGK5G53FM761765', 'Type' : 'Motorcycle', 'Year' : 2011, 'Make' : 'Ducati', 'Model' : 'Monster 1200', 'Color' : 'Yellow' }, { 'VIN' : '1HVBBAANXWH544237', 'Type' : 'Semi', 'Year' : 2009, 'Make' : 'Ford', 'Model' : 'F 150', 'Color' : 'Black' }, { 'VIN' : '1C4RJFAG0FC625797', 'Type' : 'Sedan', 'Year' : 2019, 'Make' : 'Mercedes', 'Model' : 'CLK 350', 'Color' : 'White' } >>
以下のようにデータごとにdocumentid
が出力されます。
データの問い合わせ
ロードしたデータに対して問い合わせをしてみます。基本的にSQL形式でアクセスします。
SELECT * FROM Vehicle AS v WHERE v.VIN = '1N4AL11D75C109151'
VIN | Type | Year | Make | Model | Color |
---|---|---|---|---|---|
"1N4AL11D75C109151" | "Sedan" | 2011 | "Audi" | "A5" | "Silver" |
SELECT v.VIN, r.LicensePlateNumber, r.State, r.City, r.Owners FROM Vehicle AS v, VehicleRegistration AS r WHERE v.VIN = '1N4AL11D75C109151' AND v.VIN = r.VIN
VIN | LicensePlateNumber | State | City | Owners |
---|---|---|---|---|
"1N4AL11D75C109151" | "LEWISR261LL" | "WA" | "Seattle" | {PrimaryOwner:{PersonId:""},SecondaryOwners:[]} |
SELECT * FROM Person AS p, DriversLicense as l WHERE p.GovId = l.LicenseNumber
FirstName | LastName | DOB | GovId | GovIdType | Address | LicenseNumber | LicenseType | ValidFromDate | ValidToDate | PersonId |
---|---|---|---|---|---|---|---|---|---|---|
"Alexis" | "Pena" | 1974-02-10T | "744 849 301" | "SSN" | "4058 Melrose Street, Spokane Valley, WA, 99206" | "744 849 301" | "Full" | 2017-12-06T | 2022-10-15T | "" |
"Brent" | "Logan" | 1967-07-03T | "LOGANB486CG" | "Driver License" | "43 Stockert Hollow Road, Everett, WA, 98203" | "LOGANB486CG" | "Probationary" | 2016-04-06T | 2020-11-15T | "" |
"Salvatore" | "Spencer" | 1997-11-15T | "S152-780-97-415-0" | "Passport" | "4450 Honeysuckle Lane, Seattle, WA, 98101" | "S152-780-97-415-0" | "Probationary" | 2015-08-15T | 2021-08-21T | "" |
"Melvin" | "Parker" | 1976-05-22T | "P626-168-229-765" | "Passport" | "4362 Ryder Avenue, Seattle, WA, 98101" | "P626-168-229-765" | "Learner" | 2017-08-16T | 2021-11-15T | "" |
"Raul" | "Lewis" | 1963-08-19T | "LEWISR261LL" | "Driver License" | "1719 University Street, Seattle, WA, 98109" |
データの変更
_ql_committed_
プレフィックスは対象ドキュメントのコミット済みビューを表します。ここではLewis
の初回登録したときのdocumentid
を問い合わせています。
SELECT metadata.id FROM _ql_committed_Person AS p WHERE p.data.FirstName = 'Raul' and p.data.LastName = 'Lewis'
id |
---|
"LI6ChXXLwloBp05WWwtigT" |
次にdocumentidを利用して、VehicleRegistration
テーブルへ所有者情報をセットします。
UPDATE VehicleRegistration AS r SET r.Owners.PrimaryOwner.PersonId = 'LI6ChXXLwloBp05WWwtigT' WHERE r.VIN = '1N4AL11D75C109151'
更新したデータを確認すると、documentidが登録されています。
SELECT r.Owners FROM VehicleRegistration AS r WHERE r.VIN = '1N4AL11D75C109151'
Owners |
---|
{PrimaryOwner:{PersonId:"LI6ChXXLwloBp05WWwtigT"},SecondaryOwners:[]} |
次に車をBrentからEverettへ譲渡します。
まずはBrentのdocumentidを取得します。
SELECT metadata.id FROM _ql_committed_Person AS p WHERE p.data.FirstName = 'Brent' and p.data.LastName = 'Logan'
id |
---|
"AjNBWHzeKNIHQCttIEPEbj" |
このdocumentidをPrimaryOwnerとしてVehicleRegistrationテーブルを更新します。
UPDATE VehicleRegistration AS r SET r.Owners.PrimaryOwner.PersonId = 'AjNBWHzeKNIHQCttIEPEbj', r.City = 'Everett' WHERE r.VIN = '1N4AL11D75C109151'
変更結果を確認します。
SELECT r.Owners.PrimaryOwner, r.City FROM VehicleRegistration AS r WHERE r.VIN = '1N4AL11D75C109151'
PrimaryOwner | City |
---|---|
{PersonId:"AjNBWHzeKNIHQCttIEPEbj"} | "Everett" |
次にAlexis
を副所有者として追加します。
まずはAlexis
のdocumentidを検索します。
SELECT metadata.id FROM _ql_committed_Person AS p WHERE p.data.FirstName = 'Alexis' and p.data.LastName = 'Pena'
id |
---|
"7YQHDHLkYw57osOjH3PGRG" |
取得したidをSecondaryOwnersに挿入します。ここではFROM-INSERT文を使用します。
FROM VehicleRegistration AS r WHERE r.VIN = '1N4AL11D75C109151' INSERT INTO r.Owners.SecondaryOwners VALUE { 'PersonId' : '7YQHDHLkYw57osOjH3PGRG' }
挿入したデータを確認します。
SELECT r.Owners.SecondaryOwners FROM VehicleRegistration AS r WHERE r.VIN = '1N4AL11D75C109151'
SecondaryOwners |
---|
[{PersonId:"7YQHDHLkYw57osOjH3PGRG"}] |
変更履歴を確認する
変更したデータの履歴を確認します。
まず対象車両のdocumentidを確認します。
SELECT r_id FROM VehicleRegistration AS r BY r_id WHERE r.VIN = '1N4AL11D75C109151'
r_id |
---|
"LY4HGawYaDTCQJHke8ONWI" |
取得したidを元に履歴を確認します。history関数で履歴情報を取得します。history関数では履歴の開始終了を指定することもできます。
SELECT h.data.VIN, h.data.City, h.data.Owners FROM history(VehicleRegistration) AS h WHERE h.metadata.id = 'LY4HGawYaDTCQJHke8ONWI'
VIN | City | Owners |
---|---|---|
"1N4AL11D75C109151" | "Seattle" | {PrimaryOwner:{PersonId:""},SecondaryOwners:[]} |
"1N4AL11D75C109151" | "Seattle" | {PrimaryOwner:{PersonId:"LI6ChXXLwloBp05WWwtigT"},SecondaryOwners:[]} |
"1N4AL11D75C109151" | "Everett" | {PrimaryOwner:{PersonId:"AjNBWHzeKNIHQCttIEPEbj"},SecondaryOwners:[]} |
"1N4AL11D75C109151" | "Everett" | {PrimaryOwner:{PersonId:"AjNBWHzeKNIHQCttIEPEbj"},SecondaryOwners:[{PersonId:"7YQHDHLkYw57osOjH3PGRG"}]} |
また各リビジョンのドキュメントのメタデータも検査できます。
SELECT VALUE h.metadata FROM history(VehicleRegistration) AS h WHERE h.metadata.id = 'LY4HGawYaDTCQJHke8ONWI'
id | version | txTime | txId |
---|---|---|---|
"LY4HGawYaDTCQJHke8ONWI" | 0 | 2019-09-11T01:16:31.751Z | "6mW3W78sqbsHHyryF3CGYr" |
"LY4HGawYaDTCQJHke8ONWI" | 1 | 2019-09-11T02:40:07.348Z | "EqsMZAXf6bCHOaoSP1zu9f" |
"LY4HGawYaDTCQJHke8ONWI" | 2 | 2019-09-11T02:48:09.947Z | "DThyDXjR8VJ63mv3Fa9obl" |
"LY4HGawYaDTCQJHke8ONWI" | 3 | 2019-09-11T02:56:50.003Z | "4I9LNv13L8c3C7MEBkq6Om" |
|id|システムが割り当てた一意に識別できるID| |version|| |txTime|作成された時刻| |txId|トランザクションを一意に識別できるID|
ドキュメントを検証する
Amazon QLDBはドキュメントの整合性の検証ができます。
まずクエリエディタから以下のクエリでVehicleRegistration
テーブルのビューから取得します。
SELECT r.metadata.id, r.blockAddress FROM _ql_committed_VehicleRegistration AS r WHERE r.data.VIN = '1N4AL11D75C109151'
id | blockAddress |
---|---|
"LY4HGawYaDTCQJHke8ONWI" | {strandId:"8PdWQnKPj08Cf5AVnvLMXS",sequenceNo:104} |
次に台帳自体の自体のダイジェストを取得します。
対象の台帳を選択して、ダイジェストを取得
をクリックします。
保存
をクリックして取得したダイジェストを保存します。するとダイジェストがAmazon Ionの形式でダウンロードされます。
そしてQLDBのコンソールで検証
を表示します。
取得した検証対象ドキュメントのブロックアドレス、ドキュメント IDを入力して、ダウンロードした台帳のダイジェストをアップロードします。そして検証
をクリックします。
すると検証結果が表示されます。
証明の内容は以下のようになります。
ブロックは以下のようになります。
これで更新内容の検証が行えました。
さいごに
SQLでアクセスできますが、通常のユースケースで使用するためのデータベースではありません。データの変更を厳密に管理する必要がある場合はQLDBを活用する場面が出てくるのではないかと思います。